home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / sound / driver.zip / MIDI.ASM next >
Assembly Source File  |  1987-05-11  |  38KB  |  1,065 lines

  1. page 58,132
  2. ;    file: MIDI.ASM
  3. TITLE    Z8470 MIDI driver for MSDOS
  4. SUBTTL    DESCRIPTION
  5. ;THINGS TO ADD: (DELETE THESE LINES AS YOU IMPLIMENT THEM)
  6. ;data errors reported at the time you get to that byte in read buffer
  7. ;
  8. ;
  9. ;    Loadable midi device driver for msdos.
  10. ;    Written by: Mike Higgins
  11. ;    Copyright (c) April 1984 by The Computer Entomologist.
  12. ;
  13. ;************************************************************************
  14. ;    This file implements an interrupt driven character device driver
  15. ;for the midi board described in the article: "A MIDI Project", by Jay
  16. ;Kupicky, BYTE Magazine, June 1986, V.11 N.6.  The driver will work on any
  17. ;similar board that uses the Z8470 Z80 DART chip.  This code should work
  18. ;with several DART chips on one board, if you change the appropriate
  19. ;counters, add structures and buffers for each UART, and check the interrupt
  20. ;and initialization code.
  21. ;    Why write or use such a driver?  Because the code written by
  22. ;Jay Kubicky could only be compiled on one specific C compiler, and
  23. ;required non-standard communication channels between the main code and
  24. ;the assembly driver.
  25. ;    This driver implements the MIDI board as a standard MSDOS character
  26. ;device that can be called from any program written in any language.  Even
  27. ;interpretative BASIC can open this device and perform any function that
  28. ;the board is capable of.  All the communication with the driver is
  29. ;done with standard reads,writes, and IOCTL calls.  The character input/
  30. ;output is the MIDI data, and the IOCTL data allows a program to find the
  31. ;state of the MIDI UARTS, and to perform initialization functions. (Test
  32. ;for data available or error status, flush buffers, change echo/thru
  33. ;modes).   Additionally, there is a read-with-timeout feature that
  34. ;allows the driver to return to you if no data arrives after a specified
  35. ;number of milliseconds.
  36. ;    This driver implements a 128 byte buffer on input and output to
  37. ;make catching all the MIDI data as easy as possible for you programs.
  38. ;The output buffer allows your programs to send multiple byte commands
  39. ;without having to wait on each byte: your processing can continue while
  40. ;the driver sends the bytes out one at a time for you.
  41. ;    There is also a "MIDI out/through" option in this driver that
  42. ;will copy, if enabled, any incoming bytes immediately to the associated
  43. ;MIDI out port.  I call this a "poor man's MIDI merge" because it will
  44. ;scramble MIDI commands together if they arrive at the output buffer
  45. ;too close together.  But it does have some limited applications.  Note
  46. ;that a very intelligent MIDI merge program would be easy to write as an
  47. ;application on top of this board and driver.  (Requires both UARTs hooked
  48. ;up with MIDI drivers and receivers, like mine is).
  49. ;    While initializing the uarts, this driver also initializes the on
  50. ;board clock chip.  You can get direct access to this clock for timing midi
  51. ;events, and the driver uses this clock to count time for the read-with-
  52. ;timeout feature.  The delta times are in "digital" milliseconds, so the
  53. ;value changes 1024 times each second. The two byte clock value from timer2
  54. ;will allow you to time anything that is 64 seconds or less, which should be
  55. ;long enough for any typical MIDI use. (You will have to store delta times,
  56. ;not absolute time).  On my version of the board, timer2 overflows into
  57. ;timer3, so this can be considered a 16bit extension of the regular timer.
  58. ;This results in a four byte clock that only overflows after 136
  59. ;years.  If you can keep your PC running continuously that long (without
  60. ;re-booting) I'll be very impressed!
  61. ;
  62. ;************************************************************************
  63. ;
  64. ;    Permission is hereby granted to use or distribute this software
  65. ;without any restrictions.  You may make copies for yourself or your
  66. ;friends. You may include it in any hardware or software product that you
  67. ;sell for profit.
  68. ;
  69. ;    This software is distributed as is, and is not guaranteed to work
  70. ;on any particular hardware/software configuration.  Furthermore, no 
  71. ;liability is granted with this software: the user takes responsibility for
  72. ;any damage this software may do to his system.
  73. ;
  74. ;    Nasty notices aside, if you have any questions about this software,
  75. ;you can reach me at the address below.  If you implement any new features or
  76. ;find (and fix!) any bugs, I would be happy to hear from you.
  77. ;
  78. ;    Mike Higgins
  79. ;    The Computer Entomologist
  80. ;    P.O. Box 197
  81. ;    Duncans Mills, CA 95430
  82. ;
  83. ;
  84. ;        ASSEMBLY INSTRUCTIONS:
  85. ;    MASM MIDI,MIDI,MIDI,NUL
  86. ;    LINK MIDI,MIDI,MIDI,NUL
  87. ;    EXE2BIN MIDI
  88. ;    COPY MIDI.BIN A:    (IF NOT THERE ALREADY)
  89. ;        ADD THE FOLLOWING LINE TO A:CONFIG.SYS:
  90. ;    DRIVER=MIDI.BIN
  91. ;        RE-BOOT YOUR SYSTEM AND IT'S THERE!
  92. ;
  93. ;    NOTE: 
  94. ;
  95. ;    There are  hooks in the driver for features that do not work
  96. ;    yet.  So don't expect all of these things to work just
  97. ;    because there are comments about them or bits to set for them.
  98. ;    Write me if you do implement any of this stuff.
  99. SUBTTL    DEFINITIONS
  100. PAGE
  101. ;
  102. ;        DEVICE TYPE CODES
  103. DEVCHR    EQU    08000h    ;THIS IS A CHARACTER DEVICE
  104. DEVBLK    EQU    0H    ;THIS IS A BLOCK (DISK) DEVICE
  105. DEVIOC    EQU    04000H    ;THIS DEVICE ACCEPTS IOCTRL REQUESTS
  106. DEVNON    EQU    02000H    ;NON IBM DISK DRIVER
  107. DEVSPC    EQU    010H    ;CONSOLE ACCEPTS SPECIAL INTERUPT 29
  108. DEVCLK    EQU    08H    ;THIS IS THE CLOCK DEVICE
  109. DEVNUL    EQU    04H    ;THIS IS THE NUL DEVICE
  110. DEVSTO    EQU    02H    ;THIS IS THE CURRENT STANDARD OUTPUT DEVICE
  111. DEVSTI    EQU    01H    ;THIS IS THE STANDARD INPUT DEVICE
  112. ;
  113. ;        ERROR STATUS BITS
  114. STSERR    EQU    08000H    ;GENERAL ERROR, SEE LOWER ORDER BITS FOR REASON
  115. STSBSY    EQU    0200H    ;DEVICE IS BUISY
  116. STSDNE    EQU    0100H    ;REQUEST IS COMPLETED
  117. ;        ERROR REASON VALUES FOR LOWER ORDER BITS.
  118. ERRWP    EQU    0    ;WRITE PROTECT ERROR
  119. ERRUU    EQU    1    ;UNKNOWN UNIT
  120. ERRDNR    EQU    2    ;DRIVE NOT READY
  121. ERRUC    EQU    3    ;UNKNOWN COMMAND
  122. ERRCRC    EQU    4    ;CYCLIC REDUNDANCY CHECK ERROR
  123. ERRBSL    EQU    5    ;BAD DRIVE REQUEST STRUCTURE LENGTH
  124. ERRSL    EQU    6    ;SEEK ERROR
  125. ERRUM    EQU    7    ;UNKNOWN MEDIA
  126. ERRSNF    EQU    8    ;SECTOR NOT FOUND
  127. ERRPOP    EQU    9    ;PRINTER OUT OF PAPER
  128. ERRWF    EQU    10    ;WRITE FAULT
  129. ERRRF    EQU    11    ;READ FAULT
  130. ERRGF    EQU    12    ;GENERAL FAILURE
  131. ;
  132. ;
  133. ;        DEFINE THE PORT OFFSETS AND IMPORTANT BOARD CONSTANTS
  134. MIDDAT    EQU    0    ;DATA REGESTER
  135. MIDCON    EQU    2    ;CONTROL REGESTER
  136. MIDB    EQU    1    ;BITS TO FORCE IO ADDRESS TO UART B WITH OR
  137. MIDA    EQU    NOT MIDB    ;MASK TO FORCE IO ADDR TO UART A WITH AND
  138. ;
  139. ;        BIT ASSIGNMENTS FOR THE STATUS WORD IN EACH UNIT STRUCTURE
  140. OUTINT    EQU    1    ;CHARACTER SENT OUT, INTERUPT SHOULD BE COMMING
  141. OUTHRU    EQU    2    ;INPUT IS ECHOED THRU OUTPUT (POOR MANS MIDI OUT/THRU
  142. ;
  143. ;        BIT ASSIGNMENTS FOR THE ERVAL WORD IN EACH UNIT STRUCTURE
  144. ERITMO    EQU    1    ;INPUT TIMEOUT ERROR
  145. EROTMO    EQU    2    ;OUTPUT TIMEOUT ERROR
  146. ERIBFO    EQU    4    ;INPUT BUFFER OVERFLOW
  147. EROBFO    EQU    8    ;OUPUT BUFFER OVERFLOW
  148. ERPARE    EQU    10H    ;PARITY ERROR (NOT ENABLED)
  149. ERFRAM    EQU    20H    ;FRAMING ERROR
  150. ERRXOV    EQU    40H    ;RECEVER OVERRUN (NOT LIKELY)
  151. ;
  152. ;BIT DEFINITIONS FOR ALL THE BITS/REGESTERS IN THE Z8470 ZILOG Z80 DART CHIP
  153. ;
  154.     ;WRITE REGESTER 0 BITS
  155. ZRESET    EQU    010H    ;RESET EXT/STATUS INTERUPTS
  156. ZCHANR    EQU    018H    ;CHANNEL RESET
  157. ZRXINT    EQU    020H    ;ENABLE INT ON NEXT Rx CHARACTER
  158. ZTXINT    EQU    028H    ;RESET Tx INT PENDING
  159. ZERRES    EQU    030H    ;ERROR RESET
  160. ZREINT    EQU    038H    ;RETURN FROM INT (CHANNEL A ONLY)
  161.     ;WRITE REGESTER 1 BITS
  162. W1EXTI    EQU    001H    ;EXT INT ENABLE
  163. W1TXINT    EQU    002H    ;Tx INT ENABLE
  164. W1STAT    EQU    004H    ;STATUS EFFECTS VECTOR (CHANNEL B ONLY)
  165. W1RXDIS    EQU    000H    ;Rx INT DISABLE
  166. W1RX1ST    EQU    008H    ;Rx INT ON FIRST CHARACTER
  167. W1RXALP    EQU    010H    ;Rx INT ON ALL CHARACTERS (PARITY AFFECTS VECTOR)
  168. W1RXALL    EQU    018H    ;Rx INT ON ALL CHARACTERS (PARITY DOESN'T AFF. VEC)
  169. W1WAITR    EQU    020H    ;WAIT/READY ON R/T
  170. W1WAITF    EQU    040H    ;WAIT/READY FUNCTION
  171. W1WAITE    EQU    080H    ;WAIT/READY ENABLE
  172.     ;WRITE REGESTER 3 BITS
  173. W3RXEN    EQU    001H    ;Rx ENABLE
  174. W3AUTO    EQU    020H    ;AUTO ENABLES
  175. W3RX5    EQU    000H    ;Rx 5 BITS/CHAR
  176. W3RX7    EQU    040H    ;Rx 7 BITS/CHAR
  177. W3RX6    EQU    080H    ;Rx 6 BITS/CHAR
  178. W3RX8    EQU    0C0H    ;Rx 8 BITS/CHAR
  179.     ;WRITE REGESTER 4 BITS
  180. W4PARE    EQU    001H    ;PARITY ENABLE
  181. W4PEVN    EQU    002H    ;PARITY IS EVEN
  182. W4PODD    EQU    000H    ;PARITY IS ODD
  183. W4STOP1    EQU    004H    ;ONE STOP BITS
  184. W4STP15    EQU    008H    ;1.5 STOP BITS
  185. W4STOP2    EQU    00CH    ;TWO STOP BITS
  186. W4X1    EQU    000H    ;X1 CLOCK MODE
  187. W4X16    EQU    040H    ;X16 CLOCK MODE
  188. W4X32    EQU    080H    ;X32 CLOCK MODE
  189. W4X64    EQU    0C0H    ;X64 CLOCK MODE
  190.     ;WRITE REGESTER 5 BITS
  191. W5RTS    EQU    002H    ;SET RTS BIT IN OUTPUT
  192. W5TXEN    EQU    008H    ;Tx ENABLE
  193. W5BREAK    EQU    010H    ;SEND BREAK
  194. W5TX5    EQU    000H    ;Tx 5 BITS/CHARACTER
  195. W5TX7    EQU    020H    ;Tx 7 BITS/CHAR
  196. W5TX6    EQU    040H    ;Tx 6 BITS/CHAR
  197. W5TX8    EQU    060H    ;Tx 8 BITS/CHAR
  198. W5DTR    EQU    080H    ;SET DTR BIT IN OUTPUT
  199.     ;READ REGESTER 0 BITS
  200. RXCHAR    EQU    001H    ;CHARACTER HAS BEEN RECEIVED
  201. INTPEND    EQU    002H    ;INTERUPT IS PENDING (CHANNEL A ONLY)
  202. TXFREE    EQU    004H    ;Tx BUFFER IS EMPTY
  203. CD    EQU    008H    ;CARRIER DETECT LINE IS ON
  204. RI    EQU    010H    ;RING INDICATOR LINE IS ON
  205. CTS    EQU    020H    ;CLEAR TO SEND LINE IS ON
  206. BREAK    EQU    080H    ;BREAK HAS BEEN DETECTED
  207.     ;READ REGESTER 1 BITS
  208. R1ALL    EQU    001H    ;ALL SENT (Tx BUFFER?)
  209. R1PERR    EQU    010H    ;PARRITY ERROR
  210. R1RXOVR    EQU    020H    ;Rx OFERRUN ERROR
  211. R1FRAME    EQU    040H    ;FRAMING ERROR
  212.     ;READ REGESTER 2 BITS
  213. VECMASK    EQU    006H    ;REASON-FOR-INTERUPT BITS
  214. VECB    EQU    008H    ;UART B FIRED INTERUPT
  215. ;
  216.     ;REGESTER OFFSETS AND BIT ASSIGNMENTS FOR THE CLOCK TIMER CHIP
  217. TIMER1    EQU    -3    ;OFFSET FROM CONTROL REGESTER TO TIMER DATA 1
  218. TIMER2    EQU    -2    ;TIMER 2 DATA REGESTER
  219. TIMER3    EQU    -1    ;TIMER 3
  220. T1    EQU    0    ;CONTROLLER TIMER 1 SELECT VALUE
  221. T2    EQU    040H    ;SELECT TIMER 2
  222. T3    EQU    080H    ;SELECT TIMER 3
  223. LMSB    EQU    030H    ;READ TIMER REGESTERS IN LITTLE ENDIAN MODE
  224. SHOOT    EQU    002H    ;ONE SHOT MODE
  225. RATE    EQU    004H    ;RATE GENERATOR MODE
  226. SQUARE    EQU    006H    ;SQUARE WAVE MODE
  227. LATCH    EQU    0    ;LATCH THE CURRENT VALUES FOR READING
  228. ;
  229. SUBTTL    DRIVER LIST HEAD
  230. PAGE
  231. ;*************************************************************************
  232. ;
  233. ;    BEGENING OF DRIVER CODE.
  234. ;
  235. DRIVER    SEGMENT
  236.     ASSUME    CS:DRIVER,DS:DRIVER,ES:DRIVER
  237. ;    ORG    0    ;DRIVERS START AT 0
  238. MIDIB:
  239.     DW    MIDIA,-1    ;POINTER TO NEXT DEVICE: DOS FILLS IN SEG.
  240.     DW    DEVCHR OR DEVIOC    ;CHARACTER DEVICE, IOCTL ALLOWED
  241.     DW    STRATEGY        ;OFFSET TO STRATEGY ROUTINE.
  242.     DW    REQUESTB        ;OFFSET TO "INTERUPT" ENTRYPOINT.
  243.     DB    "MIDIB   "        ;DEVICE NAME.
  244. MIDIA:
  245.     DW    -1,-1        ;POINTER TO NEXT DEVICE: END OF LINKED LIST.
  246.     DW    DEVCHR OR DEVIOC    ;THIS DEVICE IS CHARACTER IOCTL
  247.     DW    STRATEGY        ;STRATEGY ROUTINE
  248.     DW    REQUESTA        ;I/O REQUEST ROUTINT
  249.     DB    "MIDIA   "
  250.  
  251. debug    dd    0b0000000h
  252. notify    macro    char
  253. ;        local   store
  254. ;    push    es
  255. ;    push    di
  256. ;    push    ax
  257. ;    mov    al,'&char'
  258. ;    les    di,CS:debug
  259. ;    stos    byte ptr [di]
  260. ;    inc    di
  261. ;        cmp     di,80*25*2
  262. ;        jl      store
  263. ;        xor     di,di
  264. ;store:
  265. ;        mov    word ptr CS:debug,di
  266. ;        mov     al,'<'
  267. ;        stos    byte ptr es:[di]
  268. ;    pop    ax
  269. ;    pop    di
  270. ;    pop    es
  271. endm
  272.  
  273. SUBTTL DRIVER INTERNAL DATA STRUCTURES
  274. PAGE
  275. ;
  276. MIDI_UNITS    EQU    2    ;NUMBER OF UNITS THIS DRIVER IS BUILT FOR
  277. MIDI_VEC    DW    028H    ;INTERUPT VECTOR ADDRESS (NOT VECTOR NUMBER)
  278. CLOCK_PORT    DW    0FFA7H    ;PORT OF CLOCK CONTROL REGESTER
  279. ;
  280. UNIT    STRUC    ;EACH UNIT HAS A STRUCTURE DEFINING IT'S STATE:
  281. PORT    DW    ?    ;I/O PORT ADDRESS
  282. STATUS    DW    ?    ;STATUS BITS
  283. TIMEOUT    DW    ?    ;TIMEOUT CONSTANT ON READ OR WRITE REQUEST
  284. ERVAL    DW    ?    ;BIT ENCODED ERRORS SINCE LAST YOU LOOKED
  285. EROFF    DW    ?    ;OFFSET TO FIRST CHARACTER AFTER ERROR
  286. IFIRST    DW    ?    ;OFFSET TO FIRST CHARACTER IN INPUT BUFFER.
  287. IAVAIL    DW    ?    ;OFFSET TO NEXT AVAILABLE BYTE.
  288. IBUF    DW    ?    ;POINTER TO 128 BYTE INPUT BUFFER.
  289. OFIRST    DW    ?    ;OFFSET INTO FIRST CHARACTER IN OUTPUT BUFFER
  290. OAVAIL    DW    ?    ;OFFSET INTO NEXT AVAIL BYTE IN OUTPUT BUFFER
  291. OBUF    DW    ?    ;POINTER TO 128 BYTE OUTPUT BUFFER
  292. UNIT    ENDS
  293.  
  294. ;        TABLE OF STRUCTURES FOR EACH MIDI UNIT
  295. ;    THE STRUCTURES FOR ALL THE UARTS MUST BE ORGANIZED TOGETHER INTO
  296. ;    A CONTIGUOUS TABLE, BECAUSE THE INTERUPT SERVICE ROUTINE SCANS
  297. ;    THROUGH THEM WHILE DETERMINING WHICH DART FIRED THE INTERUPT.
  298. ;    SIMILARLY, THE STRUCTURES FOR THE TWO UARTS IN EACH DART MUST BI
  299. ;    IN A-B ORDER IN THIS TABLE.
  300. ;
  301. MIDI_TABA:
  302.     UNIT    <0FFA0H,0,-1,0,-1,0,0,INABUF,0,0,OUTABUF>
  303. UNIT_SIZE    EQU    $-MIDI_TABA
  304. MIDI_TABB:
  305.     UNIT    <0FFA1H,0,-1,0,-1,0,0,INBBUF,0,0,OUTBBUF>
  306.  
  307.         ;IF THE BUFFER SIZE IS A POWER OF TWO, THE PROCESS OF KEEPING
  308.         ;THE OFSETTS WITHIN THE BOUNDS OF THE BUFFER IS GREATLY
  309.         ;SIMPLIFIED.  IF YOU MODIFY THE BUFFER SIZE, KEEP IT A
  310.         ;POWER OF 2, AND MODIFY THE MASK ACCORDINGLY.
  311. BUFSIZ    EQU    128        ;INPUT BUFFER SIZE
  312. BUFMSK    EQU    127        ;MASK FOR CALCULATING OFFSETS MODULO BUFSIZ
  313. INABUF    DB    BUFSIZ DUP (?)
  314. INBBUF    DB    BUFSIZ DUP (?)
  315. OUTABUF    DB    BUFSIZ DUP (?)
  316. OUTBBUF    DB    BUFSIZ DUP (?)
  317. ;
  318. ;    STRUCTURE OF AN I/O REQUEST PACKET STATIC HEADER
  319. ;
  320. PACK    STRUC
  321. LEN    DB    ?    ;LENGTH OF RECORD
  322. PRTNO    DB    ?    ;UNIT CODE
  323. CODE    DB    ?    ;COMMAND CODE
  324. STAT    DW    ?    ;RETURN STATUS
  325. DOSQ    DD    ?    ;UNUSED DOS QUE LINK POINTER
  326. DEVQ    DD    ?    ;UNUSED DRIVER QUE LINK POINTER
  327. MEDIA    DB    ?    ;MEDIA CODE ON READ/WRITE
  328. XFER    DW    ?    ;XFER ADDRESS OFFSET
  329. XSEG    DW    ?    ;XFER ADDRESS SEGMENT
  330. COUNT    DW    ?    ;TRANSFER BYTE COUNT.
  331. PACK    ENDS
  332. ;
  333. ;    THE FOLLOWING TWO WORDS IS THE STORAGE AREA FOR THE REQUEST PACKET
  334. ;    ADDRESS, SENT TO ME BY A STRATEGY ROUTINE CALL.
  335. ;        AS REQUESTED BY THE MSDOS DRIVER MANUAL, I AM "THINKING
  336. ;    ABOUT" THE FUTURE, SO I`M DESIGNATING THIS POINTER AS THE QUEUE
  337. ;    LIST HEAD FOR REQUESTS TO THIS DRIVER.
  338. ;
  339. PACKHEAD    DD    0
  340. ;
  341. ;    THE STRATEGY ROUTINE ITSELF.
  342.     PUBLIC    STRATEGY
  343. STRATEGY    PROC    FAR
  344.             ;SQUIRREL AWAY THE POINTER FOR LATER.
  345.     MOV    WORD PTR CS:PACKHEAD,BX        ;STORE THE OFFSET,
  346.     MOV    WORD PTR CS:PACKHEAD+2,ES    ;AND THE SEGMENT.
  347.     RET
  348. STRATEGY    ENDP
  349. SUBTTL    REQUEST ROUTINES
  350. PAGE
  351.  
  352. ;        I/O REQUEST ROUTINES
  353.     PUBLIC    REQUESTA
  354. REQUESTA:        ;MIDIA HAS BEEN REQUESTED
  355.     PUSH    SI    ;SAVE SI SO YOU CAN
  356.     MOV    SI,OFFSET MIDI_TABA    ;GET THE DEVICE UNIT TABLE ADDRESS.
  357.     JMP    GEN_REQUEST    ;THE GENERIC DRIVER DOES THE REST.
  358. REQUESTB:        ;MIDIB HAS BEEN REQUESTED TO DO SOMETHING
  359.     PUSH    SI    ;SAVE SI
  360.     MOV    SI,OFFSET MIDI_TABB    ;GET UNIT TABLE TWO`S ADDRESS
  361.  
  362. GEN_REQUEST:
  363.         PUSHF        ;I REQUIRE DIRECTION FLAG CLEARED, SO I SAVE
  364.     CLD        ;THE FLAGS AND CLEAR THEM HERE.
  365.     PUSH    AX        ;SAVE ALL THE REGESTERS, YOU MAY NOT
  366.     PUSH    BX        ;NEED THEM ALL, BUT YOU WILL REGRET IT
  367.     PUSH    CX        ;IF YOU FORGET TO SAVE JUST ONE OF THEM.
  368.     PUSH    DX
  369.     PUSH    DI
  370.     PUSH    BP
  371.     PUSH    DS
  372.     PUSH    ES
  373.  
  374.     PUSH    CS        ;COPY THE CS REGESTER
  375.     POP    DS        ;INTO THE DS TO ACCESS MY DATA
  376.     LES    BX,PACKHEAD    ;RECOVER THE POINTER TO THE PACKET.
  377.     MOV    AL,ES:CODE[BX]    ;GET THE FUNCTION REQUEST CODE,
  378.     MOV    AH,0        ;MAKE IT INTO A WORD,
  379.     SAL    AX,1        ;CONVERT TO A WORD OFFSET,
  380.     MOV    DI,AX        ;AND ADD TO THE TABLE START ADDRESS
  381.     MOV    AX,STSERR OR ERRUC    ;SEND UNKNOWN COMMAND ERROR FOR EXIT
  382.     JMP    MIDI_FUNCS[DI]        ;JUMP TO THE APPROPRIATE ROUTINE
  383. ;
  384. ;        TABLE OF OFFSETS TO ALL THE DRIVER FUNCTIONS
  385. ;
  386. MIDI_FUNCS DW    MIDI_INIT    ;INITIALIZE DRIVER
  387.     DW    EXIT        ;MEDIA CHECK (BLOCK DEVICES ONLY)
  388.     DW    EXIT        ;BUILD BPB (BLOCK DEVICES ONLY)
  389.     DW    IOCTLIN        ;IOCTL INPUT
  390.     DW    READ        ;READ
  391.     DW    EXIT        ;NON-DESTRUCTIVE READ
  392.     DW    EXIT        ;INPUT STATUS
  393.     DW    EXIT        ;FLUSH INPUT BUFFER
  394.     DW    WRITE        ;WRITE
  395.     DW    WRITE        ;WRITE WITH VERIFY
  396.     DW    EXIT        ;OUTPUT STATUS
  397.     DW    EXIT        ;FLUSH OUTPUT BUFFER
  398.     DW    IOCTLOUT    ;IOCTL OUTPUT
  399. ;
  400. ;    EXIT FROM DRIVER REQUEST
  401. ;        CALL WITH AX= RETURN STATUS VALUE
  402. EXITP    PROC    FAR
  403. EXIT:
  404.     LES    BX,PACKHEAD    ;RETREIVE POINTER TO PACKET
  405. ;    OR    AX,STSDNE    ;SET THE DONE BIT IN IT.
  406.     MOV    ES:STAT[BX],AX    ;STORE THE STATUS BACK IN THE PACKET.
  407.  
  408.     POP    ES        ;RESTORE ALL THE REGESTERS
  409.     POP    DS
  410.     POP    BP
  411.     POP    DI
  412.     POP    DX
  413.     POP    CX
  414.     POP    BX
  415.     POP    AX
  416.     POPF
  417.     POP    SI
  418.     RET
  419. EXITP    ENDP
  420. SUBTTL    READ DATA REQUEST ROUTINE
  421. PAGE
  422. ;
  423. ;        ALL THE FOLLOWING ROUTINES ARE CALLED WITH THE SAME CONTEXT
  424. ;    FROM THE REQUEST ROUTINE:
  425. ;    - ES:BX POINTS TO THE I/O PACKET.
  426. ;       ROUTINES CAN MUCK UP THESE TWO REGESTERS IF THEY WANT, AS EXIT
  427. ;       WILL RESTORE THEM BEFORE IT TRIES TO SEND THE STATUS WORD BACK.
  428. ;    - CS: AND DS: POINT TO THE BEGENING OF THE DRIVER SEGMENT.
  429. ;    - DS:SI POINTS TO THE DEVICE UNIT TABLE DESCRIBING THE PARTICULAR
  430. ;       PORT BEING ACCESSED.
  431. ;    - ALL OTHER REGESTERS ARE AVAILABLE FOR USE, THE EXIT ROUTINE
  432. ;       RESTORES THEM ALL BEFORE RETURNING TO MSDOS.
  433. ;        ALL THE FOLLOWING ROUTINES SHOULD EXIT BY DOING A JMP
  434. ;    TO THE EXIT ROUTINE.  EXIT ASSUMES THAT AX
  435. ;    CONTAINS EITHER ZERO, OR THE ERROR BIT SET AND A VALID ERROR
  436. ;    RETURN VALUE IN THE LOW ORDER BITS.  EXIT SETS THE DONE BIT IN
  437. ;    THIS VALUE FOR YOU BEFORE IT RETURNS TO MSDOS.
  438. ;
  439. SUBTTL    READ REQUEST ROUTINE
  440. ;        READ DATA FROM DEVICE
  441. ;
  442. READ:
  443.     CALL    GET_CLOCK        ;GET CLOCK CURRENT VALUE FOR READ-
  444.     MOV    DX,AX            ;WITH-TIMEOUT TEST, AND STORE IT IN DX
  445.     MOV    CX,ES:COUNT[BX]        ;GET THE REQUESTED NUMBER OF BYTES
  446.     LES    DI,DWORD PTR ES:XFER[BX] ;DI IS OFFSET TO USER BUFFER
  447.     MOV    BX,TIMEOUT[SI]        ;GET THE TIMEOUT LIMIT.
  448. RLUP:
  449.     MOV    AX,EROFF[SI]        ;CHECK TO SEE IF IT'S TIME
  450.     CMP    AX,IFIRST[SI]        ;TO REPORT AN ERROR
  451.     JE    ERR_READ
  452.     CALL    GET_IN            ;GET NEXT CHAR FROM INPUT BUFFER
  453.     CMP    AH,0            ;WAS THERE ONE?
  454.     JNE    READ_WAIT        ;NO, TEST FOR TIMEOUT
  455.     STOS    BYTE PTR[DI]        ;YES,WRITE THIS BYTE OUT
  456.         ;ALTHOUGH MSDOS NEVER, TO MY KNOWLEDGE, ASKS FOR MORE THAN
  457.         ;ONE STUPID CHARACTER AT A TIME, I LOOP ON THE REQUEST SIZE
  458.         ;SO THAT THIS DRIVER WILL STILL WORK ON THAT GLORIOUS DAY
  459.         ;WHEN SOMEBODY ASKS FOR MORE THAN ONE.
  460.     LOOP    RLUP            ;KEEP GOING IF YOU WERE REQUESTED.
  461.     MOV    AX,STSDNE        ;RETURN NO ERRORS IN AX IF DONE.
  462.     JMP    EXIT
  463. ERR_READ:            ;NOW IS THE TIME TO REPORT AN INPUT ERROR
  464.     MOV    AX,-1            ;DISABLE THE ERROR CHECKING BY
  465.     MOV    EROFF[SI],AX        ;SETTING OFFSET TO FFFF
  466.     OR    ERVAL[SI],ERIBFO    ;SET INPUT BUFFER OVERFLOW BIT
  467.     JMP    ERR_RET
  468. READ_WAIT:
  469.     CMP    BX,0FFFFH        ;IF THE TIMEOUT VALUE IS MAX INT,
  470.     JE    RLUP            ;LOOP BACK UP FOREVER
  471.     CALL    GET_CLOCK
  472.     SUB    AX,DX            ;CALCULATE THE DELTA TIME
  473.     NEG    AX
  474.     CMP    AX,BX            ;COMPARE AGAINST THE TIMOUT LIMIT
  475.     JB    RLUP            ;LOOP BACK IF TIME IS STILL OK
  476.     OR    ERVAL[SI],ERITMO    ;TELL THEM A TIMEOUT ERROR HAPPENED
  477. ERR_RET:
  478.     LES    BX,PACKHEAD        ;GET IO PACKET BACK
  479.     SUB    ES:COUNT[BX],CX        ;TELL HIM HOW MANY BYTES MADE IT, AND
  480.     MOV    AX,STSBSY        ;RETURN A READ
  481.     JMP    EXIT            ;FAULT ERROR.
  482. ;
  483. SUBTTL    WRITE REQUEST ROUTINE
  484. PAGE
  485. ;        OUTPUT DATA TO DEVICE
  486. ;
  487. WRITE:
  488.     CALL    GET_CLOCK        ;GET CLOCK VALUE FOR TIMEOUT TEST
  489.     MOV    DX,AX            ;DX WILL CONTAIN STARTING TIME
  490.     MOV    CX,ES:COUNT[BX]        ;GET BYTE COUNT,
  491.     LES    DI,DWORD PTR ES:XFER[BX] ;GET XFER ADDRESS 
  492.     MOV    BX,TIMEOUT[SI]        ;GET TIMEOUT LIMIT
  493. WLUP:
  494.     MOV    AL,ES:[DI]    ;GET THE NEXT CHAR DOS SENT YOU
  495.     CALL    PUT_OUT        ;ATTEMPT TO PUT IN IN OUTPUT BUFFER
  496.     CMP    AH,0        ;DID IT WORK?
  497.     JNE    WWAIT        ;NO, GO TEST FOR TIMEOUT
  498.     INC    DI        ;SKIP TO NEXT BYTE
  499.     CALL    START_OUTPUT        ;START THE XMITTER IF NECC.
  500.     LOOP    WLUP            ;YES, GO GET NEXT CHAR.
  501.     MOV    AX,STSDNE        ;RETURN SUCCESS
  502.     JMP    EXIT
  503. WWAIT:
  504.     CALL    GET_CLOCK    ;GET CURRENT CLOCK TIME
  505.     SUB    AX,DX        ;CALCULATE NUMBER OF MILISECONDS SINCE START
  506.     NEG    AX
  507.     CMP    AX,BX        ;HAVE WE WAITED LONG ENUF?
  508.     JB    WLUP        ;NO, KEEP TRYING
  509.     OR    ERVAL[SI],EROTMO    ;TELL THEM OUTPUT TIMED OUT
  510.     LES    BX,PACKHEAD        ;GET IO PACKET BACK
  511.     SUB    ES:COUNT[BX],CX        ;TELL HIM HOW MANY BYTES MADE IT, AND
  512.     MOV    AX,STSBSY        ;YES, RETURN A WRITE FAULT ERROR
  513.     JMP    EXIT
  514. ;
  515. SUBTTL    I/O CONTROL READ REQUEST
  516. PAGE
  517. ;
  518. ;        IOCONTROL READ REQUEST, RETURN INTERNAL STRUCTURE
  519. ;
  520. IOCTLIN:
  521.     MOV    CX,ES:COUNT[BX]        ;GET THE REQUESTED NUMBER OF BYTES
  522.     MOV    DI,ES:XFER[BX]        ;DI IS OFFSET TO USER BUFFER
  523.     MOV    DX,ES:XSEG[BX]        ;SEGMENT IS LAST I NEED FROM PACKET,
  524.     MOV    ES,DX            ;NOW ES:DI POINTS TO USER BUFFER.
  525.     CMP    CX,UNIT_SIZE        ;ONLY WORKS WHEN YOU GIVE ME A
  526.     JE    DOIOCIN            ;RIGHT SIZED BUFFER TO STOMP ON.
  527.     MOV    AX,STSERR OR ERRBSL    ;RETURN AN ERROR IF NOT RIGHT.
  528.     JMP    EXIT
  529. DOIOCIN:
  530.     REP MOVSB        ;JUST COPY THE STRUCTURE TO CALLER
  531.     SUB    SI,UNIT_SIZE        ;POINT BACK AT UNIT STRUCTURE
  532.     MOV    AX,0            ;SO YOU CAN ZERO THE
  533.     MOV    ERVAL[SI],AX        ;ERROR WORD EVERY TIME THEY READ IT
  534.     MOV    AX,STSDNE        ;RETURN NO ERRORS
  535.     JMP    EXIT
  536. ;
  537. SUBTTL    I/O CONTROL WRITE REQUEST ROUTINE
  538. PAGE
  539. ;
  540. ;        I USE THIS COMMAND TO COMMUNICATE VARIOUS SPECIAL COMMANDS
  541. ;    TO THE DRIVER.  EACH BYTE SENT IS A SEPARATE COMMAND TO FLUSH A
  542. ;    BUFFER, CLEAR STATUS, SET SPECIAL FLAGS, ETC.
  543. ;
  544. IOCTLOUT:
  545.     MOV    CX,ES:COUNT[BX]        ;GET THE BYTE COUNT
  546.     LES    DI,DWORD PTR ES:XFER[BX] ;AND THE ADDRESS
  547. IOWRITE:        ;LOOP FOR ALL IO CONTROL WRITE BYTES
  548.     MOV    BL,ES:[DI]        ;GET NEXT BYTE
  549.     INC    DI            ;SKIP TO FOLLOWING ONE
  550.     AND    BX,7            ;CHOP OUT LOWER 3 BITS
  551.     SHL    BX,1            ;CONVERT TO WORD INDEX
  552.     CMP    BL,IOWSIZE        ;IS THIS A LEGAL REQUEST?
  553.     JG    IOWERR            ;NO RETURN A WRITE ERROR
  554.     JMP    WORD PTR CS:IOTAB[BX]    ;YES, EXECUTE APPROPRIATE CODE
  555. IOTAB    DW    IOWFLI        ;FLUSH INPUT BUFFER
  556.     DW    IOWFLO        ;FLUSH OUTPUT BUFFER
  557.     DW    IOWTIM        ;SET IO TIMEOUT LIMIT
  558.     DW    IOWSEO        ;SET BYTEWISE MIDI OUTPUT/THRU FLAG
  559.     DW    IOWCLO        ;CLEAR POOR MANS OUTPUT/THRU MERGE FLAG
  560. IOWSIZE    EQU    $-IOTAB
  561.  
  562. IOWFLI:                ;FLUSH INPUT BUFFER
  563.         ;PUSHF
  564.     CLI            ;CLEAR INTERUPTS WHILE YOU
  565.     MOV    AX,IFIRST[SI]    ;MANIPULATE THE BUFFER POINTERS
  566.     MOV    IAVAIL[SI],AX    ;SET AVAIL EQUAL FIRST TO EMPTY
  567.         STI
  568.     ;POPF            ;ALL DONE AND CLEAR
  569.     MOV    AX,0        ;EVERY TIME YOU FLUSH INPUT BUFFER,
  570.     MOV    ERVAL[SI],AX    ;YOU CAN CLEAR THE ERROR BITS
  571.     DEC    AX        ;AND RESET THE
  572.     MOV    EROFF[SI],AX    ;OFFSET TO ERROR TO AN ILEGAL VALUE
  573.     JMP    IOWNEXT
  574. IOWFLO:                ;FLUSH OUTPUT BUFFER
  575.         ;PUSHF
  576.     CLI            ;CLEAR INTERUPTS WHILE YOU
  577.     MOV    AX,OFIRST[SI]    ;MANIPULATE THE BUFFER POINTERS
  578.     MOV    OAVAIL[SI],AX    ;SET AVAIL EQUAL FIRST TO EMPTY
  579.         STI
  580.        ;POPF            ;ALL DONE AND CLEAR
  581.     JMP    IOWNEXT
  582. IOWTIM:                ;LOAD TIMEOUT LIMIT
  583.     MOV    AX,ES:[DI]    ;GET NEXT WORD FROM INPUT
  584.     ADD    DI,2        ;SKIP WORD IN COMMAND STRING
  585.     SUB    CX,2        ;COUNT THOSE BYTES OUT OF LOOP
  586.     JLE    IOWERR        ;IF THERE WEREN'T ENUF, SCREAM
  587.     MOV    TIMEOUT[SI],AX    ;STORE THEM IN STRUCTURE
  588.     JMP    IOWNEXT
  589. IOWSEO:                ;SET BYTEWISE MIDI OUTPUT/THRU FLAG
  590.     OR    STATUS[SI],OUTHRU    ;SET THE OUT/THRU BIT IN STATUS
  591.     JMP    IOWNEXT
  592. IOWCLO:                ;CLEAR POOR MANS MIDI OPUTPUT/THRU MERGE
  593.     AND    STATUS[SI],NOT OUTHRU    ;CLEAR THE BIT
  594.     JMP    IOWNEXT
  595. IOWNEXT:
  596.     LOOP    IOWRITE        ;LOOP FOR ALL IO CONTROL WRITE BYTES
  597.     MOV    AX,STSDNE    ;RETURN 0 FOR SUCCESS
  598.     JMP    EXIT
  599. IOWERR:            ;I/O CONTROL WRITE ERROR RETURN
  600.     MOV    AX,STSERR OR ERRBSL    ;RETURN WRITE FAULT FOR THESE
  601.     JMP    EXIT
  602.  
  603.  
  604. SUBTTL    RING BUFFER ROUTINES
  605. PAGE
  606. ;        LOCAL ROUTINES FOR MANAGING THE RING BUFFERS ON INPUT
  607. ;    AND OUTPUT.  THE FOLLOWING FOUR ROUTINES ARE ALL CALLED WITH THE
  608. ;    SAME CONTEXT:
  609. ;
  610. ;    DS:SI    POINTS TO THE UNIT STRUCTURE FOR THIS UNIT
  611. ;    AL    IS THE CHARACTER TO BE PLACED IN OR REMOVED FROM A BUFFER
  612. ;    AH    IS THE RETURN STATUS FLAG: 0=SUCESS, -1=FAILURE
  613. ;
  614. ;    ALL OTHER REGESTERS ARE PRESERVED.
  615. ;
  616. PUT_OUT    PROC    NEAR    ;PUTS AL INTO THE OUTPUT RING BUFFER
  617.     PUSH    CX
  618.     PUSH    DI
  619.     ;PUSHF
  620.     CLI            ;DISABLE INTERUPTS WHILE I HAVE OAVAIL
  621.     MOV    CX,OAVAIL[SI]    ;GET POINTER TO NEXT AVAILABLE BYTE IN
  622.     MOV    DI,CX        ;OUTPUT BUFFER.
  623.     INC    CX        ;INCRIMENT A COPY OF IT TO SEE IF THE
  624.     AND    CX,BUFMSK    ;BUFFER IS FULL.
  625.     CMP    CX,OFIRST[SI]    ;IS IT?
  626.     JE    POERR        ;YES, RETURN AN ERROR
  627.     ADD    DI,OBUF[SI]    ;NO, CALCULATE ACTUAL OFFSET OF CHAR
  628.     MOV    [DI],AL        ;AND STUFF THE CHARACTER INTO BUFFER
  629.     MOV    OAVAIL[SI],CX    ;UPDATE THE POINTER
  630.     MOV    AH,0        ;INDICATE SUCCESS
  631.     JMP    PORET        ;AND RETURN
  632. POERR:
  633.     MOV    AH,-1        ;INDICATE FAILURE.
  634. PORET:
  635.         STI
  636.     ;POPF        ;RE-ENABLE INTERUPTS
  637.     POP    DI
  638.     POP    CX
  639.     RET
  640. PUT_OUT    ENDP
  641.  
  642. GET_OUT    PROC    NEAR    ;GETS THE NEXT CHARACTER FROM OUTPUT RING BUFFER
  643.             ;SURE YOU DISABLE INTERUPTS FIRST.
  644.     PUSH    CX
  645.     PUSH    DI
  646.     ;PUSHF            ;JUST IN CASE, DISABLE INTERUPTS
  647.     CLI            ;WHILE IN THIS ROUTINE.
  648.     MOV    DI,OFIRST[SI]    ;GET POINTER TO FIRST CHARACTER TO OUTPUT
  649.     CMP    DI,OAVAIL[SI]    ;IS THE BUFFER EMPTY?
  650.     JNE    NGOERR        ;NO.
  651.     MOV    AH,-1        ;YES, INDICATE  FAILURE
  652.     JMP    GORET        ;AND RETURN
  653. NGOERR:
  654.     MOV    CX,DI        ;SAVE A COPY OF THE POINTER
  655.     ADD    DI,OBUF[SI]    ;CALCULATE ACTUAL ADDRESS
  656.     MOV    AL,[DI]        ;GET THE CHAR INTO AL
  657.     MOV    AH,0        ;INDICATE SUCCESS.
  658.     INC    CX        ;INCRIMENT THE OFFSET
  659.     AND    CX,BUFMSK    ;MODULO 128
  660.     MOV    OFIRST[SI],CX    ;STORE BACK IN UNIT TABLE.
  661. GORET:
  662.     ;POPF
  663.         STI
  664.     POP    DI
  665.     POP    CX
  666.     RET
  667. GET_OUT    ENDP
  668.  
  669. PUT_IN    PROC    NEAR    ;PUT THE CHAR FROM AL INTO INPUT RING BUFFER
  670.     PUSH    CX
  671.     PUSH    DI
  672.     ;PUSHF            ;DISABLE INTS WHILE IN THIS ROUTINE
  673.     CLI
  674.     MOV    DI,IAVAIL[SI]    ;GET POINTER TO NEXT AVAILABLE SLOT IN BUFFER
  675.     MOV    CX,DI        ;SAVE A COPY OF IT,
  676.     INC    CX        ;AND INCRIMENT THAT COPY (MODULO
  677.     AND    CX,BUFMSK        ;128) TO SEE IF THE BUFFER IS FULL.
  678.     CMP    CX,IFIRST[SI]    ;WELL, IS IT?
  679.     JNE    NPIERR        ;NO, THERE`S ROOM.
  680.     MOV    AH,-1        ;YES, INDICATE FAILURE
  681.     JMP    PIRET        ;AND RETURN
  682. NPIERR:
  683.     ADD    DI,IBUF[SI]    ;CALCULATE ACTUAL ADDRES,
  684.     MOV    [DI],AL        ;STORE THE CHARACTER THERE
  685.     MOV    IAVAIL[SI],CX    ;UPDATE THE POINTER.
  686.     MOV    AH,0        ;AND INDICATE SUCCESS.
  687. PIRET:
  688.     ;POPF
  689.         STI
  690.     POP    DI
  691.     POP    CX
  692.     RET
  693. PUT_IN    ENDP
  694.  
  695. GET_IN    PROC    NEAR    ;GETS ONE CARACTER FROM INPUT RING BUFFER INTO AL
  696.     PUSH    CX
  697.     PUSH    DI
  698.     ;PUSHF
  699.     CLI        ;DISABLE INTERUPTS WHILE I LOOK AT IFIRST.
  700.     MOV    DI,IFIRST[SI]    ;GET POINTER TO FIRST CHAR TO READ
  701.     CMP    DI,IAVAIL[SI]    ;IS THE BUFFER EMPTY?
  702.     JE    GIERR        ;THEN YOU CAN`T VERY WELL SQUEEZE WATER OUT OF IT
  703.     MOV    CX,DI        ;MAKE A COPY OF POINTER,
  704.     ADD    DI,IBUF[SI]    ;CALCULATE ACTUAL ADDRESS OF CHAR
  705.     MOV    AL,[DI]        ;GET THE CHAR INTO AL
  706.     MOV    AH,0        ;INDICATE SUCCESS
  707.     INC    CX        ;INCRIMENT THAT COPY OF YOUR POINTER,
  708.     AND    CX,BUFMSK    ;MODULO THE BUFFER SIZE,
  709.     MOV    IFIRST[SI],CX    ;SO YOU CAN UPDATE THE POINTER.
  710.     JMP    GIRET
  711. GIERR:
  712.     MOV    AH,-1        ;RETURN FAILURE INDICATOR
  713. GIRET:
  714.         STI
  715.     ;POPF        ;RE-ENABLE INTERUPTS BEFORE YOU RETURN
  716.     POP    DI
  717.     POP    CX
  718.     RET
  719. GET_IN    ENDP
  720. SUBTTL    INTERUPT SERVICE ROUTINES
  721. PAGE
  722. ;
  723. ;         THESE ROUTINES ARE ONLY CALLED WHEN AN INTERUPT IS GENERATED
  724. ;    BY THE UART.
  725. ;    THESE INTERUPT ROUTINES ARE ENVOKED WHENEVER A CHAR ARRIVES IN THE
  726. ;    UART, THE UART FINISHES SENDING A CHARACTER OUT, AN ERROR OCCURS
  727. ;    WHILE READING A CHARACTER INTO THE UART.
  728.  
  729. MIDI_INT:
  730.         PUSH    AX              ;SAVE AX FOR THE USER,
  731.     MOV    AL,020H        ;OUTPUT A 20H TO THE UNDOCUMENTED INTERUPT
  732.     OUT    020H,AL        ;CONTROL CHIP.
  733.         STI                     ;AND LET THE PROCESSOR INTERUPT ME.
  734.     PUSH    BX              ;SAVE THE REST OF THE REGESTERS.
  735.     PUSH    CX
  736.     PUSH    DX
  737.     PUSH    SI
  738.     PUSH    DI
  739.     PUSH    DS    ;SAVE THE DATA SEGMENT
  740.     PUSH    CS    ;SO YOU CAN LOAD CS
  741.     POP    DS    ;INTO DS AND FIND YOUR OWN STRUCTURES.
  742. notify I
  743. ;
  744. ;    THE FOLLOWING CODE FIGURES OUT WHICH (IF SEVERAL) DAISY-CHAINED
  745. ;    DART CHIP FIRED THE INTERUPT, WHICH UART IN THE DART IT WAS,
  746. ;    AND THE REASON FOR THE INTERUPT (RECEIVE, XMIT, ERROR).
  747. ;
  748.     MOV    CX,MIDI_UNITS    ;GET THE TOTAL NUMBER OF UARTS
  749.     MOV    SI,OFFSET MIDI_TABA    ;ADDRESS OF FIRST UARTS UNIT TABLE
  750. INT_LUP:            ;HEAD OF LOOP TO CHECK ALL UARTS
  751.     MOV    DX,PORT[SI]    ;GET THE PORT ADDRESS
  752.     ADD    DX,MIDCON    ;SLIDE UP TO COMMAND/STATUS REGESTER
  753.     MOV    AL,0        ;MAKE SURE WE ARE TALKING TO REGESTER 0
  754.     OUT    DX,AL
  755.     JMP SHORT $+2
  756.     IN    AL,DX        ;READ REGESTER 0 STATUS
  757.     TEST    AL,INTPEND    ;IS THIS DART CHIP THE ONE?
  758.     JNE    THIS_DART    ;YES, GO ON TO FIND THE PORT
  759.     ADD    SI,UNIT_SIZE*2    ;NO, SKIP TWO UARTS TO NEXT WHOLE DART
  760.     SUB    CX,2
  761.     JG    INT_LUP        ;IF THERE ARE ANY LEFT, KEEP TRYING
  762.     JMP    INT_EXIT    ;IF NOT, YOU'RE IN TROUBLE, BUT I IGNORE
  763. THIS_DART:
  764.     OR    DX,MIDB        ;SLIDE UP TO UART B ON THIS DART
  765.     MOV    AL,2        ;SET UP TO READ REGISTER 2
  766.     OUT    DX,AL
  767.     JMP    SHORT $+2
  768.     IN    AL,DX        ;READ IN VECTOR VALUE
  769.     TEST    AL,VECB        ;WAS THIS INTERUPT CAUSED BY UART B?
  770.     JNE    A_INT        ;NO, IT MUST BE UART A
  771.     DEC    CX        ;YES, MAKE SURE THERE IS A UART B STRUCTURE
  772.     JCXZ    INT_EXIT    ;IGNORE INTERUPTS FROM UNUSED UARTS
  773.     ADD    SI,UNIT_SIZE    ;GET UART B'S UNIT STRUCTURE
  774.     JMP    INT_REASON    ;WE'RE READY TO GO FIND OUT WHY
  775. A_INT:
  776.     AND    DX,MIDA        ;CONVERT PORT NUMBER BACK TO UART A
  777. INT_REASON:
  778.     AND    AX,VECMASK    ;WHACK OUT ONLY THE BITS THAT MATTER
  779.     MOV    BX,AX        ;PUT IT INTO AN INDEX REGESTER
  780.     JMP    INT_FTAB[BX]    ;JUMP TO THE APPROPRIATE ROUTINE
  781. ;
  782. INT_FTAB DW    INT_TXMIT    ;TRANSMITTER HOLDING REGESTER EMPTY
  783.     DW    INT_ERROR    ;MODEM LINE INTERUPTS NOT IMPLIMENTED
  784.     DW    INT_RECEIVE    ;RECEIVER BUFFER HAS A CHARACTER
  785.     DW    INT_ERROR    ;RECEIVER FRAMING, OVER-RUN ERROR
  786.  
  787. INT_ERROR:            ;ENTRYPOINT FOR SETTING BREAKPOINTS
  788. notify E
  789.     NOP            ;FOR ILLEGAL VECTOR VALUES
  790. INT_EXIT:
  791.     MOV    DX,PORT[SI]    ;GET THE PORT NUMBER AGAIN
  792.     ADD    DX,MIDCON    ;CHANGE TO CONTROL PORT
  793.     AND    DX,MIDA        ;MAKE SURE IT'S CHANNEL A OF DART
  794.     MOV    AL,ZREINT    ;SEND THE SIMULATED Z80 RETURN
  795.     OUT    DX,AL        ;FROM INTERUPT COMMAND TO DART
  796. ;INTERUPT STUF WAS HERE
  797.         POP    DS    ;RECOVER ALL THE REGESTERS
  798.     POP    DI
  799.     POP    SI
  800.     POP    DX
  801.     POP    CX
  802.     POP    BX
  803.     POP    AX
  804.     IRET
  805. ;
  806. ;        THE FOLLOWING INTERUPT SERVICE ROUTINES ALL HAVE THE
  807. ;    SAME CONTEXT:
  808. ;    -CS AND DS POINT TO THE DRIVER SEGMENT.
  809. ;    -DS:[SI] POINTS TO THE UNIT STRUCTURE FOR THE ASYNC LINE THAT
  810. ;        FIRED THE INTERUPT.
  811. ;    -DX POINTS TO THE CONTROL REGESTER OF THE DART
  812. ;    -AX BX CX AND DI ARE AVAILABLE FOR SCRATCH.  ALL OTHERS
  813. ;        MUST BE LEFT ALONE OR SAVED AND RECOVERED.
  814. ;    TO EXIT FROM AN INTERUPT SERVICE ROUTINE, THESE SERVERS MUST
  815. ;    JUMP TO INT_EXIT.
  816. ;
  817. SUBTTL    RECEIVER INTERUPT SERVICE ROUTINE
  818. PAGE
  819. INT_RECEIVE:    ;THE PORT HAS RECEIVED A NEW CHARACTER, I MUST COPY IT
  820.         ;INTO THE INPUT TYPEAHEAD BUFFER.
  821. notify R
  822.         SUB    DX,MIDCON    ;POINT AT THE DATA REGESTER
  823.     IN    AL,DX        ;GET THE CHARACTER
  824.     CALL    PUT_IN        ;PUT THE CHARACTER IN THE RING BUFFER
  825.     CMP    AH,0        ;WAS THERE ROOM?
  826.     JE    REC_OK        ;NO, SET FLAGS BEFORE RETURNING
  827. notify O
  828.     OR    ERVAL[SI],ERIBFO ;NO, SET THE OVERFLOW BIT
  829.     CMP    EROFF[SI],-1    ;IF YOU HAVN'T INDICATED ANY ERRORS YET
  830.     JNE    REC_OK
  831.     PUSH    AX    
  832.     MOV    AX,IAVAIL[SI]    ;THEN INDICATE THE POSITION THAT THE
  833.     MOV    EROFF[SI],AX    ;ERROR HAPPENED
  834.     POP    AX
  835. REC_OK:
  836.     TEST    STATUS[SI],OUTHRU    ;POOR MANS MIDI MERGE TURNED ON?
  837.     JZ    NEXT_REC        ;NOPE, DON'T ECHI THIS CHAR
  838.     CALL    PUT_OUT            ;YUP, SEND THE CHARACTER TO OUTPUT
  839.     CALL    START_OUTPUT        ;AND START THE TRANSMIT IF NECC.
  840. NEXT_REC:            ;CHECK TO SEE IF THERE ARE MORE CHARS READY
  841.     ADD    DX,MIDCON    ;POINT BACK AT THE COMMAND REGESTER
  842.     IN    AL,DX        ;READ REGESTER 0
  843.     TEST    AL,RXCHAR    ;IS THERE ANOTHER CHARACTER READY?
  844.     JNZ    INT_RECEIVE    ;YES, GO READ IT IN ALSO
  845.     MOV    AL,ZRXINT    ;NO, ENABLE INTERUPTS ON NEXT CHAR
  846.     OUT    DX,AL        ;THAT DOES COME IN.
  847.     JMP    INT_EXIT
  848. ;
  849. ;
  850. SUBTTL    SPECIAL RECEIVE CONDITION INTERUPT
  851. PAGE
  852. ;
  853. ;        PARITY, FRAMING, OR OVERRUN ERROR INTERUPT
  854. INT_RXSTAT:
  855. notify S
  856.     MOV    AL,1        ;REQUEST READ REGESTER 1
  857.     OUT    DX,AL
  858.     JMP SHORT $+2
  859.     IN    AL,DX        ;READ IN THE VALUE
  860.     AND    AX,070H        ;CHOP OUT THE ERROR BITS
  861.     OR    ERVAL[DI],AX    ;ADD THOSE BITS TO THE ERROR WORD
  862.     CMP    EROFF[SI],-1    ;CHECK TO SEE IF YOU'VE ALREADY
  863.     JNE    RXSTAT_DONE    ;TRAPPED ONE UNREPORTED ERROR
  864.     MOV    AX,IAVAIL[SI]    ;IF NOT, THEN POINT THE
  865.     MOV    EROFF[SI],AX    ;ERROR OFFSET AT NEXT AVAIL BYTE
  866. RXSTAT_DONE:
  867.     MOV    AL,ZERRES    ;RESET THE ERROR BITS IN THE
  868.     OUT    DX,AL        ;UART.
  869.     JMP    INT_EXIT
  870.  
  871. SUBTTL    TRANSMITTER INTERUPT SERVICE ROUTINE
  872. PAGE
  873. ;        THE TRANSMITTER HOLDING REGESTER IS EMPTY, LOOK TO SEE IF
  874. INT_TXMIT:    ;THERE ARE MORE CHARS TO PRINT NOW.
  875. notify T
  876.         AND    STATUS[SI],NOT OUTINT    ;CLEAR INTERUPT EXPECTED BIT.
  877.     CALL    START_OUTPUT        ;START THE NEXT CHARACTER
  878.     MOV    AL,ZTXINT        ;RESET THE UART TRANSMITTER
  879.     OUT    DX,AL            ;PENDING BIT
  880.     JMP    INT_EXIT
  881.  
  882. ;ROUTINE TO START THE NEXT CHARACTER PRINTING ON THE PORT, IF OUTPUT
  883. ;IS NOT BEING SUSPENDED FOR ONE REASON OR ANOTHER.
  884. ;THIS ROUTINE MAY BE CALLED FROM REQUEST ROUTINES, OR FROM INTERUPT
  885. ;SEVICE ROUTINES.
  886. ;    THIS ROUTINE DESTROYS AX AND DX.
  887. ;    SI MUST POINT AT THE UNIT STRUCTURE.
  888. START_OUTPUT    PROC    NEAR
  889.     ;PUSHF                ;SAVE THE FLAGS SO I CAN
  890.     CLI                ;DISABLE INTERUPTS
  891.     TEST    STATUS[SI],OUTINT    ;AM I IN HOLD OUTPUT MODE?
  892.     JNE    DONT_START        ;YES, DON'T SEND ANY MORE CHARS.
  893.     CALL    GET_OUT        ;CHECK TO SEE IF THERE IS A CHAR IN THE BUF
  894.     CMP    AH,0        ;WELL, WAS THERE?
  895.     JNE    DONT_START    ;NO, BUFFER IS EMPTY
  896.     MOV    DX,PORT[SI]    ;YES, POINT DX AT THE TX OUT REGISTER
  897.     OUT    DX,AL        ;SEND HIM THE CHARACTER
  898.     OR    STATUS[SI],OUTINT        ;WARN EVERYBODY THAT I'M BUSY.
  899. DONT_START:
  900.     ;POPF
  901.         STI
  902.     RET
  903. START_OUTPUT    ENDP
  904. ;;
  905. SUBTTL    READ CLOCK CURRENT VALUE
  906. PAGE
  907. ;
  908. ;        THE GET_CLOCK ROUTINE RETURNS THE CURRENT CLOCK VALUE
  909. ;    IN AX.  REGESTER DX IS PRESERVED.
  910. ;    This routine returns the "unsullied" clock value, so beware! The
  911. ;    timer chip is a count DOWN timer, and you should calculate delta
  912. ;    values accordingly.  The C routines that read this clock invert
  913. ;    the current value to make it look like an increasing time count,
  914. ;    but down here in the driver I left the values raw.
  915. ;
  916. GET_CLOCK    PROC    NEAR
  917.     PUSH    DX
  918.     MOV    DX,CLOCK_PORT    ;GET PORT OF CLOCK CHIP
  919.     MOV    AL,T2 OR LATCH    ;TELL HIM YOU WANT TO LATCH TIMER 2
  920.     OUT    DX,AL
  921.     JMP SHORT $+2
  922.     ADD    DX,TIMER2    ;POINT DX AT TIMER2
  923.     IN    AL,DX        ;READ TIMER2 LSB
  924.     JMP SHORT $+2
  925.     MOV    AH,AL
  926.     IN    AL,DX        ;READ TIMER2 MSB
  927.     XCHG    AH,AL        ;SWAP THE BYTES TO NORMAL
  928.     POP    DX
  929.     RET
  930. GET_CLOCK    ENDP
  931. ;
  932. ;
  933. ;        THE FOLLOWING LABEL DEFINES THE END OF THE DRIVER, SO I
  934. ;        CAN TELL DOS HOW BIG I AM.
  935. MIDI_END:
  936. SUBTTL    MIDI INITIALIZATION REQUEST ROUTINE
  937. PAGE
  938. ;
  939. ;        THE INITIALIZE DRIVER ROUTINES ARE STORED AFTER THE "END"
  940. ;    OF THE DRIVER HERE SO THAT THIS CODE CAN BE THROWN AWAY AFTER
  941. ;    THE DEVICE HAS BEEN INITIALIZED.  THIS CODE IS ONLY CALLED TWICE:
  942. ;    ONCE TO INITIALIZE EACH OF THE MIDI UNITS THAT THIS DRIVER
  943. ;    CONTAINS.  (APPARENTLY, MSDOS DOESN'T WRITE ANYTHING ON TOP OF
  944. ;    THIS CODE UNTIL ALL UNITS ARE INITIALIZED.
  945. ;        THE CONTEXT OF THE INITIALIZE CODE BELOW IS THE SAME AS
  946. ;    ALL THE OTHER REQUEST ROUTINES EARLIER IN THE DRIVER.
  947. ;
  948. INIT_TAB    DB    ZCHANR        ;TABLE OF INITIALIZATION BYTES
  949.         DB    1,W1TXINT OR W1RX1ST OR W1STAT    ;ENABLEL XMITR INTS
  950.         DB    3,W3RXEN OR W3RX8    ;ENABLE RECEIVING, 8 BITS/CHARACTER
  951.         DB    4,W4STOP1 OR W4X64    ;1 STOP BIT, X64 CLOCK MODE
  952.         DB    5,W5TXEN OR W5TX8    ;8 BITS/CHARACTER ON INPUT
  953.         DB    ZRXINT        ;ALLOW NEXT CHAR INPUT TO INTERUPT
  954. INIT_SIZE    EQU    $-INIT_TAB    ;COUNT THE BYTES FOR ME
  955. ;
  956. ;    TABLE OF REGESTER OFFSETS AND VALUES FOR INITIALIZING THE CLOCK
  957. CLK_TAB    DB    0,T1 OR LMSB OR RATE    ;TIMER ONE IS A RATE GENERATOR
  958.     DB    TIMER1,0,0,8        ;THAT DIVIDES CLOCK BY 2048 (800H)
  959.     DB    -TIMER1,T2 OR LMSB OR RATE ;TIMER TWO IS ALSO A RATE GENERATOR
  960.     DB    TIMER2,0FFH,0,0FFH    ;THAT DIVIDES BY THE LARGEST INT.
  961.     DB    -TIMER2,T3 OR LMSB OR RATE    ;TIMER 3 JUST COUNTS DOWN
  962.     DB    TIMER3,0FFH,0,0FFH    ;FROM LARGEST INTEGER (64K)
  963. CLK_SIZE =    $-CLK_TAB        ;SIZE OF THIS TABLE
  964. ;
  965. ;        INITIALIZE THE DRIVER AND DEVICE
  966. ;
  967. MIDI_INIT:
  968.     MOV    AX,OFFSET MIDI_END    ;GET THE SIZE OF THE DRIVER
  969.     MOV    ES:XFER[BX],AX        ;SEND THAT BACK IN PACKET
  970.     MOV    ES:XSEG[BX],CS        ;SEND THE CODE SEGMENT ALSO.
  971.                 ;I HAVE SATISFIED ALL THE REQIREMENTS OF THE
  972.                 ;INIT FUNCTION TO RETURN IN THE I/O PACKET, SO
  973.                 ;I CAN DESTROY THE CONTENTS OF ES:BX AND USE
  974.                 ;THEM FOR OTHER THINGS.
  975.     MOV    DX,PORT[SI]        ;GET THE PORT ADDRESS OF THIS LINE
  976.     ADD    DX,MIDCON        ;SLIDE UP TO THE COMMAND REGESTER
  977. ;            THE FOLLOWING CODE INITIALIZES THE Z8470 DART
  978. ;            FOR INTERUPT DRIVEN MIDI OPERATION
  979.     MOV    BX,OFFSET INIT_TAB    ;GET ADDRESS OF TABLE
  980.     MOV    CX,INIT_SIZE        ;SIZE OF TABLE
  981. INIT_PORT:
  982.     MOV    AL,[BX]            ;GET NEXT PORT CONTROL BYTE
  983.     OUT    DX,AL            ;SEND IT TO CONTROL REGESTER
  984.     JMP SHORT $+2
  985.     INC    BX            ;INCRIMENT TO NEXT BYTE
  986.     LOOP    INIT_PORT        ;LOOP UNTIL DONE
  987.  
  988.     TEST    DX,MIDB            ;IF THIS IS UART B OF THE DART,
  989.     JNE    DONE_INIT        ;THEN YOU ARE DONE
  990.             ;ONLY ONCE PER DART IS IT NESESSARY TO INITIALIZE
  991.             ;THE VECTOR IN REGESTER 2
  992.     OR    DX,MIDB            ;SLIDE UP TO UART B
  993.     MOV    AL,2            ;AND THEN INITIALIZE THE
  994.     OUT    DX,AL            ;WRITE REGESTER 2 WITH
  995.     JMP SHORT $+2
  996.     MOV    AL,0            ;A ZERO VECTOR NUMBER
  997.     OUT    DX,AL
  998.             ;IT'S ONLY NECESSARY TO INITIALIZE THE INTERUPT
  999.             ;VECTOR AND MASK ONCE, BUT I ALLOW THE FOLLOWING
  1000.             ;CODE TO EXECUTE ONCE FOR EVERY DART CHIP ON YOUR
  1001.             ;BOARD.  IT CAN'T HURT.
  1002.     MOV    AX,0            ;POINT ES AT THE VECTOR SEGMENT
  1003.     MOV    ES,AX            ;SO CAN INITIALIZE THE VECTORS
  1004.     MOV    AX,OFFSET MIDI_INT    ;GET ADRS OF INTERUPT SERVICE ROUTINE
  1005.     MOV    DI,MIDI_VEC        ;GET ADRS OF VECTOR
  1006.     STOS    WORD PTR [DI]        ;STORE THE OFFSET THERE, THEN
  1007.     MOV    ES:[DI],CS        ;THE SEGMENT IN THE FOLLOWING WORD.
  1008.     MOV    CX,DI            ;CALCULATE THE VECTOR NUMBER:
  1009.     SUB    CL,022H            ;SUBTRACT BIAS TO HARDWARE INTS,
  1010.     SAR    CL,1            ;DIVIDE BY 4 TO CONVERT TO
  1011.     SAR    CL,1            ;HARDWARE INTERUPT NUMBER.
  1012.     MOV    AH,1            ;SHIFT A MASK BY THAT MUCH TO
  1013.     SAL    AH,CL            ;CREATE INTERUPT ENABLE MASK BIT,
  1014.     NOT    AH            ;WHICH IS ACTIVE LOW...
  1015.     IN    AL,021H            ;GET SYSTEM HARDWARE INTERUPT MASK
  1016.     JMP SHORT $+2
  1017.     AND    AL,AH            ;AND MY BIT OUT OF IT,
  1018.     OUT    021H,AL            ;WRITE IT BACK OUT AGAIN.
  1019. ;
  1020. ;        THIS CODE INITIALIZES THE ON-BOARD CLOCK CHIP TO USEFUL
  1021. ;    MODES AND RATES FOR MIDI USE.  TIMER1 IS USED TO DIVIDE THE CLOCK
  1022. ;    PULSE DOWN BY 2048 TO A 1024 TICKS PER SECOND (ABOUT 1 MILISECOND)
  1023. ;    RATE.  THAT RATE FEEDS TIMER2 WHICH IS THE FIRST CLOCK VALUE READ.
  1024. ;    FOR LONG DELAY PERIODS, TIMER2 IS ASSUMED TO FEED TIMER3, SO IT
  1025. ;    WILL CONTAIN THE OVERFLOW, ALLOWING COUNTS UP TO 137 YEARS!
  1026. ;
  1027.     MOV    DX,CLOCK_PORT        ;GET CLOCK CONTROL REGESTER PORT
  1028.     MOV    BX,OFFSET CLK_TAB    ;GET ADDRS OF TABLE OF COMMANDS
  1029.     MOV    CX,CLK_SIZE/2        ;SEND THIS MANY COMMANDS TO CLOCK
  1030. ;    SAR    CX,1
  1031. CLK_LUP:
  1032.     MOV    AL,[BX]            ;GET NEXT REGESTER OFFSET
  1033.     CBW
  1034.     ADD    DX,AX            ;AND OFFSET FROM IT
  1035.     INC    BX            ;SLIDE UP TO NEXT VALUE
  1036.     MOV    AL,[BX]            ;GET IT, AND
  1037.     INC    BX            ;DON'T FORGET TO SKIP PAST
  1038.     OUT    DX,AL            ;SEND VALUE TO CLOCK REGESTER
  1039.     LOOP    CLK_LUP
  1040. ;            THE FOLLOWING LOOP IS SUGESTED BY INTEL IN THE
  1041. ;            CLOCK CHIP SPEC.'S TO MAKE SURE THE CLOCK HAS
  1042. ;            SETTLED BEFORE YOU FIRST READ IT.  I ONLY WATCH
  1043. ;            TIMER2 SETTLE, AND ASSUME THE REST ARE LONG GONE
  1044.     MOV    DX,CLOCK_PORT        ;MAKE SURE YOU'RE LOOKING AT CONTROL
  1045. CLK_START:
  1046.     MOV    AL,T2 OR LATCH        ;LATCH TIMER TWO
  1047.     OUT    DX,AL
  1048.     JMP SHORT $+2
  1049.     ADD    DX,TIMER2        ;SLIDE UP TO IT'S DATA REGESTER
  1050.     IN    AL,DX            ;READ AND
  1051.     JMP SHORT $+2
  1052.     MOV    AH,AL            ;SAVE LEASTMOST BYTE
  1053.     IN    AL,DX            ;READ MOST SIGNIFICANT BYTE
  1054.     SUB    DX,TIMER2        ;SLIDE BACK TO CONTROL REGESTER
  1055.     XCHG    AL,AH            ;MAKE INTO A BINARY NUMBER
  1056.     NOT    AX            ;REVERSE INTO A UP-COUNTING NUMBER
  1057.     OR    AX,AX            ;AND CHECK FOR ZERO
  1058.     JNZ    CLK_START        ;WAIT FOR IT TO SETTLE DOWN
  1059. DONE_INIT:
  1060.     MOV    AX,STSDNE        ;RETURN NO ERRORS.
  1061.     JMP    EXIT
  1062.  
  1063. DRIVER    ENDS
  1064.     END
  1065.